package com.cygsunri.wisdompark.util;

import com.cygsunri.wisdompark.callback.entity.DeviceTemplateData;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * modbus数据处理
 **/
@Slf4j
public class ModbusDataUtil {


    /**
     * 雅达电表modbus数据处理
     *
     * @param
     * @param base64Str 报文
     */
    public static Map<String, Object> yadaModbusData(String base64Str, List<DeviceTemplateData> deviceTemplateDataList) throws Exception {
        //解析报文
        String decryptStr = Base64Util.decryptBASE64ToString(base64Str);
        log.info(decryptStr);
        Map<String, DeviceTemplateData> deviceTemplateDataMap = deviceTemplateDataList.stream().collect(Collectors.toMap(DeviceTemplateData::getRegisterAddress, deviceTemplateData -> deviceTemplateData));
        String[] byteStr = stringToStringArray(decryptStr, 2);
        //1 校验报文
        checkYadaDecryptStr(decryptStr, byteStr);
        //2 报文数据解析成Map
        Map<String, Object> resultMap = getYadaMap(decryptStr, byteStr, deviceTemplateDataMap);
        //3 加上时间戳
        resultMap.put("time", LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli());
        return resultMap;
    }

    /**
     * 雅达报文数据解析成Map
     */
    private static Map<String, Object> getYadaMap(String decryptStr, String[] byteStr, Map<String, DeviceTemplateData> deviceTemplateDataMap) {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("begin", 0);
        //头部报文解析
        getYadaHeadMap(byteStr, resultMap);
        //数据域报文解析
        getYadaDataFieldMap(byteStr, resultMap, deviceTemplateDataMap);
        resultMap.remove("length");
        resultMap.remove("begin");
        resultMap.remove("value");
        return resultMap;
    }


    /**
     * 安科瑞报文数据解析成Map
     */
    private static Map<String, Object> getAnkeruiMap(String decryptStr, String[] byteStr, Map<Integer, DeviceTemplateData> deviceTemplateDataMap) throws Exception {
        Map<String, Object> resultMap = new HashMap<>();
        //截取第一段和第二段
        String reg = "5B5B([\\s\\S]*?)5D5D";
        //利用正则表达式定义规则，a开头中间除了a任意都获取，b结尾
        Pattern p = Pattern.compile(reg);
        Matcher m = p.matcher(decryptStr);
        int indexNumber = 0;
        while (m.find()) {
            resultMap.put("begin", -1);
            String str = m.group();
            log.info(str);
            if (str.length() < 30) {
                throw new Exception("安科瑞报文格式错误：" + decryptStr);
            }
            String headerStr = str.substring(0, 18);
            String dataStr = str.substring(18, str.length() - 12);
            String crcStr = str.substring(str.length() - 12, str.length() - 8);
            log.info(headerStr);
            log.info(dataStr);
            log.info(crcStr);
            byte[] bytes = Byte16Util.toByteArray("0103" + dataStr);
            String crcCheck = CRCCheckUtil.Make_CRC(bytes);
            //crc校验
            if (crcCheck.toUpperCase().equals(crcStr)) {
                String[] dataArray = stringToStringArray(dataStr, 2);
                int dataNum = NumberUtil.hex16To10(getMap(dataArray, resultMap, 1).get("value").toString());
                int i = 0;
                while (i < dataNum) {
                    DeviceTemplateData temp = deviceTemplateDataMap.get(indexNumber);
                    String value = getMap(dataArray, resultMap, temp.getDataSize()).get("value").toString();
                    if (temp.getSaveRedis() != null && temp.getSaveRedis()) {
                        resultMap.put(temp.getName(), NumberUtil.hex16To10(value));
                    }
                    log.info(temp.getName() + "::" + temp.getComments() + "::" + indexNumber + "::" + i + "::" + dataNum);
                    i += temp.getDataSize();
                    indexNumber++;
                }
            }
        }
        resultMap.remove("begin");
        resultMap.remove("value");
        return resultMap;
    }

    /**
     * 雅达报文解析--数据域报文解析
     */
    private static void getYadaDataFieldMap(String[] byteStr, Map<String, Object> resultMap, Map<String, DeviceTemplateData> deviceTemplateDataMap) {
        int length = Integer.valueOf(resultMap.get("length").toString());
        //数据域
        String data = getMap(byteStr, resultMap, length).get("value").toString();
        //解析数据域
        String[] dataByte = stringToStringArray(data, 2);
        resultMap.put("begin", -1);
        //协议类型
        String protocolType = getMap(dataByte, resultMap, 1).get("value").toString();
//        resultMap.put("protocolType", protocolType);
        //时标：
        String time = getMap(dataByte, resultMap, 6).get("value").toString();
//        resultMap.put("time", time);
        //设备号
        String equipmentNum = getMap(dataByte, resultMap, 1).get("value").toString();
//        resultMap.put("equipmentNum", equipmentNum);
        //数据标识个数
        int dataNum = NumberUtil.hex16To10(getMap(dataByte, resultMap, 1).get("value").toString());
//        resultMap.put("dataNum", dataNum);
        //获取数据标识和值
        getYadaDataMap(dataByte, resultMap, deviceTemplateDataMap, dataNum);

    }

    /**
     * 雅达报文解析--获取数据标识和值
     */
    private static void getYadaDataMap(String[] dataByte, Map<String, Object> resultMap,
                                       Map<String, DeviceTemplateData> deviceTemplateDataMap, int dataNum) {
        for (int i = 0; i < dataNum; i++) {
            String dataName = getMap(dataByte, resultMap, 2).get("value").toString().toUpperCase();
            int baowenSize = NumberUtil.hex16To10(getMap(dataByte, resultMap, 1).get("value").toString().toUpperCase());
            int dataSize = deviceTemplateDataMap.get("0x" + dataName).getDataSize();
            String dataType = deviceTemplateDataMap.get("0x" + dataName).getDataType();
            //获取data值 todo
            String dataString = getMap(dataByte, resultMap, 2 * dataSize).get("value").toString().toUpperCase();
            Object data = getData(dataString, dataType);
            Boolean saveRedis = deviceTemplateDataMap.get("0x" + dataName).getSaveRedis();
            if (saveRedis != null && saveRedis) {
                resultMap.put(deviceTemplateDataMap.get("0x" + dataName).getName(), data);
            }
            log.info(i + ":" + dataName + ":" + data);
            int j = dataSize;
            while (baowenSize > j) {
                dataName = NumberUtil.hex10To16(NumberUtil.hex16To10(dataName) + dataSize).toUpperCase();
                dataSize = deviceTemplateDataMap.get("0x" + dataName).getDataSize();
                dataType = deviceTemplateDataMap.get("0x" + dataName).getDataType();
                saveRedis = deviceTemplateDataMap.get("0x" + dataName).getSaveRedis();
                //获取data值 todo
                dataString = getMap(dataByte, resultMap, 2 * dataSize).get("value").toString().toUpperCase();
                data = getData(dataString, dataType);
                if (saveRedis != null && saveRedis) {
                    resultMap.put(deviceTemplateDataMap.get("0x" + dataName).getName(), data);
                }
                j += dataSize;
                log.info(i + ":" + baowenSize + ":" + j + ":" + dataName + ":" + data);
            }
        }
    }

    /**
     * 报文解析--根据报文类型和报文获取值
     */
    private static Object getData(String dataString, String dataType) {
        if (dataType.equals("float")) {
            Float data = NumberUtil.hex16To10Float(dataString);
            return data;
        } else {
            int data = NumberUtil.hex16To10(dataString);
            return data;
        }
    }

    /**
     * 雅达报文解析--头部报文解析
     */
    private static void getYadaHeadMap(String[] byteStr, Map<String, Object> resultMap) {
        //设备编码
        String equipmentCode = getMap(byteStr, resultMap, 6).get("value").toString();
//        resultMap.put("equipmentCode", equipmentCode);
        //控制码
        String controlCode = getMap(byteStr, resultMap, 1).get("value").toString();
//        resultMap.put("controlCode", controlCode);
        //数据域长度
        int length = NumberUtil.hex16To10(getMap(byteStr, resultMap, 1).get("value").toString());
        resultMap.put("length", length);
    }

    /**
     * 雅达报文校验
     */
    private static void checkYadaDecryptStr(String decryptStr, String[] byteStr) throws Exception {
        //1.1校验起始符和结束符
        if (!decryptStr.startsWith("68") || !decryptStr.endsWith("16")) {
            log.error("雅达报文格式错误,报文：{}", decryptStr);
            throw new Exception("雅达报文格式错误");
        }
        //1.2 校验报文长度

        if (byteStr.length < 11) {
            log.error("雅达报文长度不够,报文：{}", decryptStr);
            throw new Exception("雅达报文长度不够");
        }
        //1.3 获取数据域长度
        //数组 0：起始符 1-6：设备编码 7：功能码 8：数据域长度 9-N：数据域 N+1：校验码 N+2：结束符
        //获取数据域长度
        int length = NumberUtil.hex16To10(byteStr[8]);
        boolean lengthResult = (length + 11) == byteStr.length;
        if (!lengthResult) {
            log.error("雅达报文数据域长度错误,报文：{}", decryptStr);
            throw new Exception("雅达报文数据域长度错误");
        }
    }


    public static String[] stringToStringArray(String src, int length) {
        //检查参数是否合法
        if (null == src || src.equals("")) {
            return null;
        }

        if (length <= 0) {
            return null;
        }
        int n = (src.length() + length - 1) / length; //获取整个字符串可以被切割成字符子串的个数
        String[] split = new String[n];
        for (int i = 0; i < n; i++) {
            if (i < (n - 1)) {
                split[i] = src.substring(i * length, (i + 1) * length);
            } else {
                split[i] = src.substring(i * length);
            }
        }
        return split;
    }

    public static Map<String, Object> getMap(String[] first, Map<String, Object> map, int length) {
        String value = "";
        int begin = Integer.valueOf(map.get("begin").toString());
        for (int i = 0; i < length; i++) {
            begin = begin + 1;
            value += first[begin];
        }
        map.put("begin", begin);
        map.put("value", value);
        return map;
    }

    /**
     * 安科瑞电表modbus数据处理
     *
     * @param
     * @param base64Str 报文
     */
    public static Map<String, Object> ankeruiModbusData(String base64Str, List<DeviceTemplateData> deviceTemplateDataList) throws Exception {
        //解析报文
        String decryptStr = Base64Util.decryptBASE64ToString(base64Str);
        log.info(decryptStr);
        Map<Integer, DeviceTemplateData> deviceTemplateDataMap = deviceTemplateDataList.stream().collect(Collectors.toMap(DeviceTemplateData::getIndexNumber, deviceTemplateData -> deviceTemplateData));
        String[] byteStr = stringToStringArray(decryptStr, 2);
        //1 校验报文
        checkAnkeruiDecryptStr(decryptStr, byteStr);
        //2 报文数据解析成Map
        Map<String, Object> resultMap = getAnkeruiMap(decryptStr, byteStr, deviceTemplateDataMap);
        //3 加上时间戳
        resultMap.put("time", LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli());
        return resultMap;
    }

    /**
     * 安科瑞报文校验
     */
    private static void checkAnkeruiDecryptStr(String decryptStr, String[] byteStr) throws Exception {
        //1.1校验起始符和结束符
        if (!decryptStr.startsWith("7B7B915B5B") || !decryptStr.endsWith("7D7D")) {
            log.error("安科瑞报文格式错误,报文：{}", decryptStr);
            throw new Exception("安科瑞报文格式错误");
        }
        //1.2 校验报文长度
        if (byteStr.length < 11) {
            log.error("安科瑞报文长度不够,报文：{}", decryptStr);
            throw new Exception("安科瑞报文长度不够");
        }
    }
}
